/*
 * Created: Author: Aléx Carvalho / Date: 25/abr/2015
 * 
 * Changed: $Author: ac280593@gmail.com $ / $Date: 2015-06-11 01:31:49 -0300 (Thu, 11 Jun 2015) $
 */
package com.cpm.view.zk.tree.structure;

import com.cpm.ServiceLocator;
import com.cpm.entities.Folder;
import com.cpm.entities.Indicator;
import com.cpm.entities.Node;
import com.cpm.view.zk.tree.DefaultTreeNode;
import com.cpm.wrapper.Position;
import java.util.HashSet;
import java.util.Set;

public class StructureTreeControlDrag {

    public static void executeDrag(int posY, int height, DefaultTreeNode<Node> draggedTreeNode, DefaultTreeNode<Node> treeNode) {
        if (!DefaultTreeNode.isAncestor(draggedTreeNode, treeNode)) {

            boolean sameLevel = treeNode.getParent() == draggedTreeNode.getParent();

            if (posY < 10) { // cima
                if (treeNode.getParent().getData() instanceof Indicator && draggedTreeNode.getData() instanceof Folder) {
                    System.out.println("Não pode colocar pasta em baixo de indicador");
                }
                else if (sameLevel) {
                    // TODO: tratar se não vai trocar a posição para não fazer update sem precisar
                    dragTopSameLevel(treeNode, draggedTreeNode);
                }
                else {
                    dragTopDifferentLevel(treeNode, draggedTreeNode);
                }
            }
            else if (posY > 20) { // baixo 
                if (treeNode.getParent().getData() instanceof Indicator && draggedTreeNode.getData() instanceof Folder) {
                    System.out.println("Não pode colocar pasta em baixo de indicador");
                }
                else if (sameLevel) {
                    dragBottomSameLevel(treeNode, draggedTreeNode);
                }
                else {
                    dragBottomDifferentLevel(treeNode, draggedTreeNode);
                }

            }
            else { // meio
                if (treeNode.getData() instanceof Indicator && draggedTreeNode.getData() instanceof Folder) {
                    System.out.println("Não pode colocar pasta em baixo de indicador");
                }
                else {
                    dragMiddle(treeNode, draggedTreeNode);
                }
            }
        }
    }

    private static void dragBottomDifferentLevel(DefaultTreeNode<Node> treeNode, DefaultTreeNode<Node> draggedTreeNode) {
         Set<Node> updateNodes = new HashSet<>(1);

        Node node = treeNode.getData();
        Node nodeDragged = draggedTreeNode.getData();

        // atualizar a posicao dos child do antigo parent
        draggedTreeNode.getParent().getChildren().forEach(child -> {
            Node nodeChild = child.getData();

            if (nodeChild.getPosition().larger(draggedTreeNode.getData().getPosition())) {
                nodeChild.decrementPosition();

                updateNodes.add(nodeChild);
            }
        });

        // Incrementa a posição dos nodos maiores que o selecionado
        treeNode.getParent().getChildren().forEach(child -> {
            Node nodeChild = child.getData();

            if (nodeChild.getPosition().larger(node.getPosition())) {
                nodeChild.incrementPosition();

                updateNodes.add(nodeChild);
            }
        });

        // Atualiza os dados do draged
        nodeDragged.setPosition(node.getPosition().increment());
        nodeDragged.setParentId(node.getParentId());
        nodeDragged.setParentFamily(node.getParentFamily());

        updateNodes.add(node);

        updatePosition(updateNodes);
        updatePositionAndParent(nodeDragged);

        // Atualiza na localização na arvore
        draggedTreeNode.removeFromParent();
        treeNode.getParent().insert(draggedTreeNode, treeNode.getParent().getIndex(treeNode) + 1);
    }

    private static void dragBottomSameLevel(DefaultTreeNode<Node> treeNode, DefaultTreeNode<Node> draggedTreeNode) {
        Set<Node> updateNodes = new HashSet<>(1);

        Node node = treeNode.getData();
        Node nodeDragged = draggedTreeNode.getData();

        Position positionNode = node.getPosition();
        Position positionDragged = nodeDragged.getPosition();

        if (positionNode.larger(positionDragged)) { // Movendo nodo para posição maior
            treeNode.getParent().getChildren().forEach(child -> {
                Node nodeChild = child.getData();
                Position positionChild = nodeChild.getPosition();

                if (positionChild.larger(positionDragged) && positionChild.smaller(positionNode)) {
                    nodeChild.decrementPosition();

                    updateNodes.add(nodeChild);
                }
            });

            nodeDragged.setPosition(positionNode);
            node.decrementPosition();
           
            // atualiza arvore
            draggedTreeNode.removeFromParent();
            treeNode.getParent().insert(draggedTreeNode, treeNode.getParent().getIndex(treeNode) + 1);
        }
        else { // Movendo nodo para posição menor
            treeNode.getParent().getChildren().forEach(child -> {
                Node nodeChild = child.getData();
                Position positionChild = nodeChild.getPosition();

                if (positionChild.smaller(positionDragged) && positionChild.larger(positionNode)) {
                    nodeChild.incrementPosition();

                    updateNodes.add(nodeChild);
                }
            });

            // position do nodoDrag = (selecionado + 1)
            nodeDragged.setPosition(positionNode.increment());

            draggedTreeNode.removeFromParent();
            treeNode.getParent().insert(draggedTreeNode, treeNode.getParent().getIndex(treeNode)+1);
        }

        updateNodes.add(nodeDragged);

        updatePosition(updateNodes);
    }

    private static void dragTopDifferentLevel(DefaultTreeNode<Node> treeNode, DefaultTreeNode<Node> draggedTreeNode) {
        Set<Node> updateNodes = new HashSet<>(1);

        Node node = treeNode.getData();
        Node nodeDragged = draggedTreeNode.getData();

        // atualizar a posicao dos child do antigo parent
        draggedTreeNode.getParent().getChildren().forEach(child -> {
            Node nodeChild = child.getData();

            if (nodeChild.getPosition().larger(draggedTreeNode.getData().getPosition())) {
                nodeChild.decrementPosition();

                updateNodes.add(nodeChild);
            }
        });

        // Incrementa a posição dos nodos maiores que o selecionado
        treeNode.getParent().getChildren().forEach(child -> {
            Node nodeChild = child.getData();

            if (nodeChild.getPosition().larger(node.getPosition())) {
                nodeChild.incrementPosition();

                updateNodes.add(nodeChild);
            }
        });

        // Atualiza os dados do draged
        nodeDragged.setPosition(node.getPosition());
        nodeDragged.setParentId(node.getParentId());
        nodeDragged.setParentFamily(node.getParentFamily());

        // Atualiza posição do selecionado
        node.incrementPosition();

        updateNodes.add(node);

        updatePosition(updateNodes);
        updatePositionAndParent(nodeDragged);

        // Atualiza na localização na arvore
        draggedTreeNode.removeFromParent();
        treeNode.getParent().insert(draggedTreeNode, treeNode.getParent().getIndex(treeNode));
    }

    private static void dragTopSameLevel(DefaultTreeNode<Node> treeNode, DefaultTreeNode<Node> draggedTreeNode) {
        Set<Node> updateNodes = new HashSet<>(1);

        Node node = treeNode.getData();
        Node nodeDragged = draggedTreeNode.getData();

        Position positionNode = node.getPosition();
        Position positionDragged = nodeDragged.getPosition();

        if (positionNode.larger(positionDragged)) { // Movendo nodo para posição maior
            treeNode.getParent().getChildren().forEach(child -> {
                Node nodeChild = child.getData();
                Position positionChild = nodeChild.getPosition();

                if (positionChild.larger(positionDragged) && positionChild.smaller(positionNode)) {
                    nodeChild.decrementPosition();

                    updateNodes.add(nodeChild);
                }
            });

            // position do nodoDrag = (selecionado - 1)
            nodeDragged.setPosition(positionNode.decrement());

            draggedTreeNode.removeFromParent();
            treeNode.getParent().insert(draggedTreeNode, treeNode.getParent().getIndex(treeNode) + 1);
        }
        else { // Movendo nodo para posição menor
            treeNode.getParent().getChildren().forEach(child -> {
                Node nodeChild = child.getData();
                Position positionChild = nodeChild.getPosition();

                if (positionChild.smaller(positionDragged) && positionChild.larger(positionNode)) {
                    nodeChild.incrementPosition();

                    updateNodes.add(nodeChild);
                }
            });

            // position do nodoDrag = selecionado)
            nodeDragged.setPosition(positionNode);
            node.incrementPosition();

            // Atualiza na localização na arvore
            draggedTreeNode.removeFromParent();
            treeNode.getParent().insert(draggedTreeNode, treeNode.getParent().getIndex(treeNode));
        }

        updateNodes.add(nodeDragged);

        updatePosition(updateNodes);
    }

    private static void dragMiddle(DefaultTreeNode<Node> treeNode, DefaultTreeNode<Node> draggedTreeNode) {
        Set<Node> updateNodes = new HashSet<>(1);

        Node node = treeNode.getData();
        Node nodeDragged = draggedTreeNode.getData();

        // Atualiza a position dos nodos do antigo parent
        draggedTreeNode.getParent().getChildren().forEach(child -> {
            Node nodeChild = child.getData();

            if (nodeChild.getPosition().larger(node.getPosition())) {
                nodeChild.decrementPosition();

                updateNodes.add(nodeChild);
            }
        });

        // Atualiza a position dos childs do novo parent
        treeNode.getChildren().forEach(child -> {
            Node nodeChild = child.getData();

            nodeChild.incrementPosition();
            updateNodes.add(nodeChild);
        });

        // Atualiza o parent e position
        nodeDragged.setPosition(new Position(1));
        nodeDragged.setParentId(node.getId());
        nodeDragged.setParentFamily(node.getFamily());

        updatePosition(updateNodes);
        updatePositionAndParent(nodeDragged);

        // Atualiza na localização na arvore
        draggedTreeNode.removeFromParent();
        treeNode.insert(draggedTreeNode, 0);
    }

    private static void updatePosition(Set<Node> nodes) {
        ServiceLocator.getNodeRepository().updatePosition(nodes);
//        nodes.forEach( (Node node) -> { 
//            ServiceLocator.getNodeRepository().updatePosition(node);
//        });
    }

    private static void updatePositionAndParent(Node node) {
    ServiceLocator.getNodeRepository().updatePositionAndParent(node);
    }
}
